/**
 * 
 */
package uk.co.droidinactu.inlibrislibertas.model;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import uk.co.droidinactu.common.Base64;
import uk.co.droidinactu.common.model.AbstractDataObj;
import uk.co.droidinactu.inlibrislibertas.LibraryConstants;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.provider.BaseColumns;
import android.util.Log;

/**
 * @author andy
 * 
 */
public class EBook extends AbstractDataObj {

	private static final ArrayList<String> coverImgNames = new ArrayList<String>();

	public static final String DATABASE_TABLE_NAME = "ebook";
	public static final String FIELD_NAME_APPLICATION_ID = "application_id";
	public static final String FIELD_NAME_author_sort = "author_sort";
	public static final String FIELD_NAME_authors = "authors";
	public static final String FIELD_NAME_book_producer = "book_producer";
	public static final String FIELD_NAME_BOOK_TITLE = "title";
	public static final String FIELD_NAME_category = "category";
	public static final String FIELD_NAME_comments = "comments";
	public static final String FIELD_NAME_cover = "cover";
	public static final String FIELD_NAME_db_id = "db_id";
	public static final String FIELD_NAME_ddc = "ddc";
	public static final String FIELD_NAME_isbn = "isbn";
	public static final String FIELD_NAME_language = "language";
	public static final String FIELD_NAME_lcc = "lcc";
	public static final String FIELD_NAME_lccn = "lccn";
	public static final String FIELD_NAME_lpath = "lpath";
	public static final String FIELD_NAME_mime = "mime";
	public static final String FIELD_NAME_pubdate = "pubdate";
	public static final String FIELD_NAME_publication_type = "publication_type";
	public static final String FIELD_NAME_publisher = "publisher";
	public static final String FIELD_NAME_rating = "rating";
	public static final String FIELD_NAME_rights = "rights";
	public static final String FIELD_NAME_series = "series";
	public static final String FIELD_NAME_series_index = "series_index";
	public static final String FIELD_NAME_size = "size";
	public static final String FIELD_NAME_tags = "tags";
	public static final String FIELD_NAME_thumbnail_data = "thumbnail_data";
	public static final String FIELD_NAME_thumbnail_height = "thumbnail_height";
	public static final String FIELD_NAME_thumbnail_width = "thumbnail_width";
	public static final String FIELD_NAME_timestamp = "timestamp";
	public static final String FIELD_NAME_title_sort = "title_sort";
	public static final String FIELD_NAME_uuid = "uuid";
	private static String LOG_TAG = "InLibris_EBook:";

	/**
	 * this number denotes the version of this class. so that we can decide if
	 * the structure of this class is the same as the one being deserialised
	 * into it
	 */
	private static final long serialVersionUID = 1L;

	static {
		EBook.coverImgNames.add("cover.jpeg");
		EBook.coverImgNames.add("cover1.jpeg");
		EBook.coverImgNames.add("cover.jpg");
		EBook.coverImgNames.add("cover1.jpg");
		EBook.coverImgNames.add("cover_image.jpg");
	}

	public static String[] getFieldNames() {
		return new String[] { BaseColumns._ID, AbstractDataObj.FIELD_NAME_LAST_UPDATED, FIELD_NAME_BOOK_TITLE,
				FIELD_NAME_APPLICATION_ID, FIELD_NAME_author_sort, FIELD_NAME_book_producer, FIELD_NAME_category,
				FIELD_NAME_comments, FIELD_NAME_cover, FIELD_NAME_db_id, FIELD_NAME_ddc, FIELD_NAME_isbn,
				FIELD_NAME_language, FIELD_NAME_lcc, FIELD_NAME_lccn, FIELD_NAME_lpath, FIELD_NAME_mime,
				FIELD_NAME_publication_type, FIELD_NAME_pubdate, FIELD_NAME_publisher, FIELD_NAME_rating,
				FIELD_NAME_rights, FIELD_NAME_series, FIELD_NAME_series_index, FIELD_NAME_size, FIELD_NAME_timestamp,
				FIELD_NAME_title_sort, FIELD_NAME_uuid, FIELD_NAME_thumbnail_height, FIELD_NAME_thumbnail_width,
				FIELD_NAME_thumbnail_data, FIELD_NAME_authors, FIELD_NAME_tags };
	}

	public int application_id = -1;
	public String author_sort = "";

	public HashMap<String, String> author_sort_map = new HashMap<String, String>();
	public ArrayList<Author> authors = new ArrayList<Author>();

	public String authorString = "";
	public String book_producer = "";
	public String category = "";
	public HashMap<String, String> classifiers = new HashMap<String, String>();
	public String comments = "";
	public String cover = "";
	public List<String> cover_data = new ArrayList<String>();
	private Bitmap coverImg = null;
	public String db_id = "";
	public String ddc = "";
	protected final DecimalFormat decFmt = new DecimalFormat("#0.0");
	public int id = -1;
	public String isbn = "";
	public String language = "";
	public List<String> languages = new ArrayList<String>();
	public String lcc = "";
	public String lccn = "";
	public String lpath = "";
	public String mime = "";
	public String pubdate = "";
	public String publication_type = "";
	public String publisher = "";
	public int rating = -1;
	public String rights = "";
	private String series = "";
	private String series_index = "";
	private int size = -1;
	public ArrayList<Tag> tags = new ArrayList<Tag>();
	public String tagString = "";
	public CalibreThumbnail thumbnail = new CalibreThumbnail();
	public String timestamp = "";
	public String title = "";
	public String title_sort = "";

	public List<String> user_metadata = new ArrayList<String>();

	public String uuid = "";

	public EBook() {
	}

	public EBook(final Cursor aRow) {
		title = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_BOOK_TITLE));
		application_id = aRow.getInt(aRow.getColumnIndex(EBook.FIELD_NAME_APPLICATION_ID));
		author_sort = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_author_sort));
		book_producer = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_book_producer));
		category = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_category));
		comments = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_comments));
		cover = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_cover));
		db_id = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_db_id));
		ddc = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_ddc));
		isbn = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_isbn));
		language = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_language));
		lcc = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_lcc));
		lccn = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_lccn));
		lpath = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_lpath));
		mime = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_mime));
		pubdate = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_pubdate));
		publication_type = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_publication_type));
		publisher = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_publisher));
		rating = aRow.getInt(aRow.getColumnIndex(EBook.FIELD_NAME_rating));
		rights = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_rights));
		setSeries(aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_series)));
		setSeriesIndex(aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_series_index)));
		setSize(aRow.getInt(aRow.getColumnIndex(EBook.FIELD_NAME_size)));
		title_sort = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_title_sort));
		uuid = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_uuid));
		authorString = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_authors));
		tagString = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_tags));

		thumbnail = new CalibreThumbnail();
		thumbnail.data = aRow.getString(aRow.getColumnIndex(EBook.FIELD_NAME_thumbnail_data));
		thumbnail.height = aRow.getInt(aRow.getColumnIndex(EBook.FIELD_NAME_thumbnail_height));
		thumbnail.width = aRow.getInt(aRow.getColumnIndex(EBook.FIELD_NAME_thumbnail_width));

		setUniqueIdentifier(aRow.getString(aRow.getColumnIndex(BaseColumns._ID)));
		setLastUpdated(aRow.getString(aRow.getColumnIndex(AbstractDataObj.FIELD_NAME_LAST_UPDATED)));
	}

	private boolean doCompare(final FilterEntry fe, final String strToCheck) {
		if (strToCheck.toLowerCase().contains(fe.token.toLowerCase())) {
			return true;
		}
		return false;
	}

	public String getAuthors() {
		String authorLst = "";
		for (final Author auth : authors) {
			authorLst += auth.getAuthorName() + ", ";
		}
		try {
			authorLst = authorLst.substring(0, authorLst.length() - 2);
		} catch (final StringIndexOutOfBoundsException e) {
		}
		return authorLst;
	}

	@Override
	public ContentValues getContentValues() {
		final ContentValues map = super.getContentValues();
		map.put(EBook.FIELD_NAME_BOOK_TITLE, title);
		map.put(EBook.FIELD_NAME_APPLICATION_ID, application_id);
		map.put(EBook.FIELD_NAME_author_sort, author_sort);
		map.put(EBook.FIELD_NAME_book_producer, book_producer);
		map.put(EBook.FIELD_NAME_category, category);
		map.put(EBook.FIELD_NAME_comments, comments);
		map.put(EBook.FIELD_NAME_cover, cover);
		map.put(EBook.FIELD_NAME_db_id, db_id);
		map.put(EBook.FIELD_NAME_ddc, ddc);
		map.put(EBook.FIELD_NAME_isbn, isbn);
		map.put(EBook.FIELD_NAME_language, language);
		map.put(EBook.FIELD_NAME_lcc, lcc);
		map.put(EBook.FIELD_NAME_lccn, lccn);
		map.put(EBook.FIELD_NAME_lpath, lpath);
		map.put(EBook.FIELD_NAME_mime, mime);
		map.put(EBook.FIELD_NAME_publication_type, publication_type);
		map.put(EBook.FIELD_NAME_pubdate, pubdate);
		map.put(EBook.FIELD_NAME_publisher, publisher);
		map.put(EBook.FIELD_NAME_rating, rating);
		map.put(EBook.FIELD_NAME_rights, rights);
		map.put(EBook.FIELD_NAME_series, series);
		map.put(EBook.FIELD_NAME_series_index, series_index);
		map.put(EBook.FIELD_NAME_size, size);
		map.put(EBook.FIELD_NAME_timestamp, timestamp);
		map.put(EBook.FIELD_NAME_title_sort, title_sort);
		map.put(EBook.FIELD_NAME_uuid, uuid);
		map.put(EBook.FIELD_NAME_thumbnail_height, thumbnail.height);
		map.put(EBook.FIELD_NAME_thumbnail_width, thumbnail.width);
		map.put(EBook.FIELD_NAME_thumbnail_data, thumbnail.data);
		map.put(EBook.FIELD_NAME_authors, authorString);
		map.put(EBook.FIELD_NAME_tags, tagString);
		return map;
	}

	public synchronized Bitmap getCoverImg(final Context ctx, final int noImgId, final int noImgIdEpub,
			final int noImgIdPdf) {
		if (coverImg != null) {
			return coverImg;
		}
		Bitmap tmpBmp = null;
		try {
			tmpBmp = getCoverImgFromThumbnail(ctx);
			if (tmpBmp != null) {
				coverImg = tmpBmp;
				return coverImg;
			} else if (lpath.endsWith("epub")) {
				tmpBmp = getCoverImgFromEpub(ctx);
				if (tmpBmp != null) {
					coverImg = tmpBmp;
					return coverImg;
				}
			} else if (lpath.endsWith("pdf")) {
				tmpBmp = getCoverImgFromPdf(ctx);
				if (tmpBmp != null) {
					coverImg = tmpBmp;
					return coverImg;
				}
			}
		} catch (final Exception e) {
		}
		return BitmapFactory.decodeResource(ctx.getResources(), noImgId);
	}

	private Bitmap getCoverImgFromEpub(final Context ctx) {
		try {
			// create a buffer to improve copy performance later.
			final byte[] buffer = new byte[2048];

			ZipInputStream zipinputstream = null;
			ZipEntry zipentry;
			zipinputstream = new ZipInputStream(new FileInputStream("/sdcard/" + lpath));

			while ((zipentry = zipinputstream.getNextEntry()) != null) {
				// for each entry to be extracted
				final String entryName = zipentry.getName();
				// System.out.println("File ::" + entryName);

				if (EBook.coverImgNames.contains(entryName)) {
					// Once we get the entry from the stream, the stream is
					// positioned read to read the raw data, and we keep
					// reading until read returns 0 or less.
					final String outpath = "/sdcard/CoverImg.jpg";
					FileOutputStream output = null;
					try {
						output = new FileOutputStream(outpath);
						int len = 0;
						while ((len = zipinputstream.read(buffer)) > 0) {
							output.write(buffer, 0, len);
						}
					} finally {
						// we must always close the output file
						if (output != null) {
							output.close();
						}
					}
					zipinputstream.closeEntry();

					final Bitmap bmp = BitmapFactory.decodeFile(outpath);
					final File f = new File(outpath);
					f.delete();
					return bmp;
				}
			}// while

			zipinputstream.close();
		} catch (final Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private Bitmap getCoverImgFromPdf(final Context ctx) {
		final Bitmap mBitmap = null;
		try {
			final File file = new File("/sdcard/" + lpath);

		} catch (final Exception e) {
		}
		return null;
	}

	private Bitmap getCoverImgFromThumbnail(final Context ctx) {
		Bitmap bmp = null;
		if (thumbnail.data.length() > 0) {
			byte[] imgBytes = null;
			try {
				// this.thumbnail.data.length()
				imgBytes = Base64.toBytes(thumbnail.data);
				if (imgBytes != null) {
					bmp = BitmapFactory.decodeByteArray(imgBytes, 0, imgBytes.length);
				}
				if (bmp == null) {
					final String outpath = "/sdcard/tmpCoverImg.jpg";
					writeImageBytesToFile(imgBytes, outpath);
					bmp = BitmapFactory.decodeFile(outpath);
					final File f = new File(outpath);
					f.delete();
				}
			} catch (final Exception e) {
				final String msg = e.getMessage();
				Log.e(LibraryConstants.LOG_TAG + EBook.LOG_TAG, msg, e);
			}
		}
		return bmp;
	}

	@Override
	public HashMap<Integer, ArrayList<String>> getFields() {
		final HashMap<Integer, ArrayList<String>> fields = super.getFields();

		int x = fields.size();
		fields.put(x++, getArrayList(EBook.FIELD_NAME_BOOK_TITLE, "VARCHAR(255) NOT NULL"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_APPLICATION_ID, "VARCHAR(255) NOT NULL"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_authors, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_author_sort, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_book_producer, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_category, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_comments, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_cover, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_db_id, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_ddc, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_isbn, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_language, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_lcc, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_lccn, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_lpath, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_mime, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_publication_type, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_pubdate, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_publisher, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_rating, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_rights, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_series, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_series_index, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_size, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_timestamp, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_title_sort, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_uuid, "VARCHAR(255)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_thumbnail_data, "VARCHAR(1024)"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_thumbnail_width, "INTEGER"));
		fields.put(x++, getArrayList(EBook.FIELD_NAME_thumbnail_height, "INTEGER"));
		return fields;
	}

	public String getSelectAllSeries() {
		return "select DISTINCT (" + EBook.FIELD_NAME_series + ") from " + getTableName();
	}

	public String getSelectSql(final SortOrder sOrdr) {
		String orderby = EBook.FIELD_NAME_BOOK_TITLE;
		if (sOrdr.equals(SortOrder.Author)) {
			orderby = EBook.FIELD_NAME_author_sort;
		} else if (sOrdr.equals(SortOrder.Author)) {
			orderby = EBook.FIELD_NAME_series;
		}

		return "select * from " + getTableName() + " order by " + orderby;
	}

	public String getSelectSqlFiltered() {
		return "select * from " + getTableName(); // + " where " +
		// AbstractDataObj.FIELD_NAME_UNIQUE_IDENTIFIER
		// +
		// "=" + objId;
	}

	public String getSeries() {
		return series;
	}

	public String getSeriesIndex() {
		return series_index;
	}

	public String getSeriesString() {
		if (!series.equals("null") && series.length() > 0) {
			return "book " + series_index + " of " + series;
		}
		return " ";
	}

	public String getSizeString() {
		String sizeStr = "" + size + " Bytes";
		if (size > 1000000) {
			sizeStr = decFmt.format(size / 1000000) + " Mb";
		} else if (size > 1000) {
			sizeStr = decFmt.format(size / 1000) + " Kb";
		}
		return sizeStr;
	}

	public String getSqlSelectBookFromTitleAndAuthor() {
		return "select * from " + getTableName() + " where " + EBook.FIELD_NAME_BOOK_TITLE + "=\'" + title + "\' and "
				+ EBook.FIELD_NAME_author_sort + "=\'" + author_sort + "\'";
	}

	public String getSqlUpdateFromV001() {
		// TODO Auto-generated method stub
		return null;
	}

	public String getSqlUpdateFromV002() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getTableName() {
		return EBook.DATABASE_TABLE_NAME;
	}

	public CharSequence getTagList() {
		String txtTags = "";
		for (final Tag tg : tags) {
			txtTags += tg.name + " & ";
		}
		try {
			txtTags = txtTags.substring(0, txtTags.length() - 2);
		} catch (final StringIndexOutOfBoundsException e) {
		}
		return txtTags;
	}

	public synchronized boolean matchesFilter(final FilterEntry fe) {
		boolean titleMatch = false;
		boolean publisherMatch = false;
		boolean seriesMatch = false;
		final boolean authorMatch = false;
		final boolean tagMatch = false;

		if (doCompare(fe, title)) {
			titleMatch = true;
			return true;
		} else if (doCompare(fe, publisher)) {
			publisherMatch = true;
			return true;

		} else if (doCompare(fe, series)) {
			seriesMatch = true;
			return true;
		}
		if (!(titleMatch || publisherMatch)) {
			// for (final Author author : this.authors) {
			// if (this.doCompare(fe, author.name)) {
			// authorMatch = true;
			// return true;
			// }
			// }
			// for (final Tag tag : this.tags) {
			// if (this.doCompare(fe, tag.name)) {
			// tagMatch = true;
			// return true;
			// }
			// }
		}
		return seriesMatch || titleMatch || publisherMatch || authorMatch || tagMatch;
	}

	public void readMetadata() {
		if (lpath.endsWith("epub")) {
			readMetaDataFromEpub();
		}
	}

	private void readMetaDataFromEpub() {
		// try {
		//
		// // create a buffer to improve copy performance later.
		// final byte[] buffer = new byte[2048];
		//
		// ZipInputStream zipinputstream = null;
		// final ZipEntry zipentry;
		// zipinputstream = new ZipInputStream(new FileInputStream("/sdcard/" +
		// this.lpath));
		//
		// final EpubReader epbrdr = new EpubReader();
		// final Book bk = epbrdr.readEpub(zipinputstream);
		// zipinputstream.close();
		//
		// this.convertMetadata(bk);
		// } catch (final Exception e) {
		// e.printStackTrace();
		// }
	}

	public void setAuthors(final ArrayList<String> bookAuthorList) {
		authorString = "";
		for (final String s : bookAuthorList) {
			authorString += "||" + s;
			authors.add(new Author(s));
		}
	}

	public void setSeries(final String value) {
		series = value;
	}

	public void setSeriesIndex(final String value) {
		series_index = value;
	}

	public void setSize(final int size) {
		this.size = size;
	}

	public void setTags(final ArrayList<String> bookTagList) {
		tagString = "";
		for (final String s : bookTagList) {
			tagString += "||" + s;
			tags.add(new Tag(s));
		}
	}

	@Override
	public String toString() {
		return title + " [book " + series_index + " of " + series + "]\nby " + getAuthors();
	}

	private void writeImageBytesToFile(final byte[] imgBytes, final String strFilePath) {
		try {
			FileOutputStream fos = new FileOutputStream(strFilePath);
			fos.write(imgBytes);
			fos.flush();
			fos.close();
			fos = null;
			System.gc();
		} catch (final FileNotFoundException ex) {
			System.out.println("FileNotFoundException : " + ex);
		} catch (final IOException ioe) {
			System.out.println("IOException : " + ioe);
		}
	}

}
